using System;
using System.Linq;
using System.Collections.Generic;
using System.IO;

namespace LightPersist.Storage
{
	public abstract class BaseStorage : IStorage
	{
		protected const string StrErrObjAlreadyExists = "Object already exists in database";
		protected const string StrErrObjDoesNotExists = "Object does not exists in database";
		bool _disposed;

		protected BaseStorage() {}

		protected BaseStorage(string connectionString)
		{
			Connect(connectionString);
		}

		protected string CurrentConectionString { get; private set; }

		protected bool Connected
		{
			get { return !string.IsNullOrEmpty(CurrentConectionString); }
		}

		#region IStorage Members

		public void Connect(string connectionString)
		{
			Aspect();
			if (connectionString == null)
			{
				throw new ArgumentNullException("connectionString");
			}

			if (CurrentConectionString == null)
			{
				CurrentConectionString = connectionString;
			}
			else
			{
				throw new InvalidOperationException("Storage Already Connected");
			}

			OnConnected();
		}

		protected virtual void OnConnected(){}

		public void CreateObject(ObjectState obj)
		{
			ObjectAccessCoreContract(obj);
			CommitInsert(obj);
		}

		public void LoadObject(ObjectState obj)
		{
			ObjectAccessCoreContract(obj);
			obj.ClearObject();
			LoadObjectCore(obj);
		}

//		public void LoadProperty(ObjectState obj, string propertyName)
//		{
//			ObjectAccessCoreContract(obj);
//			if (string.IsNullOrEmpty(propertyName))
//			{
//				throw new ArgumentNullException("propertyName", "null or empty");
//			}
//			LoadPropertyCore(obj, propertyName);
//		}

		public void DeleteObject(ObjectState obj)
		{
			ObjectAccessCoreContract(obj);
			CommitRemove(obj);
		}

		public virtual void UpdateObject(ObjectState obj)
		{
			ObjectAccessCoreContract(obj);
			CommitUpdate(obj);
		}

		public virtual bool RequestAccess(ObjectState obj, AccessMode accessMode)
		{
			ObjectAccessCoreContract(obj);
			return false;
		}

		public virtual AccessMode GetAccess(ObjectState obj)
		{
			ObjectAccessCoreContract(obj);
			return AccessMode.Unknown;
		}

		public virtual void Dispose()
		{
			_disposed = true;
		}

		public IEnumerable<Guid> GetObjectIDs()
		{
			EnsureStorageConnected();
			return GetObjectIDsCore();
		}

		public bool IsExists(ObjectState obj)
		{
			EnsureStorageConnected();
			return IsExistsCore(obj);
		}

		#endregion

		public abstract void CommitRemove(ObjectState obj);
		public abstract void CommitInsert(ObjectState obj);
		public abstract void CommitUpdate(ObjectState obj);
		public abstract void LoadObjectCore(ObjectState obj);
		// public abstract void LoadPropertyCore(ObjectState obj, string name);
		public abstract bool IsExistsCore(ObjectState obj);

		/// <summary>
		/// EnsureStorageConnected() && Aspect() included
		/// </summary>
		/// <param name="obj"></param>
		protected void ObjectAccessCoreContract(ObjectState obj)
		{
			EnsureStorageConnected();
			if (obj.Identity == default(Guid))
			{
				throw new ApplicationException("Object state have empty Identity");
			}
		}

		void Aspect()
		{
			if (_disposed)
			{
				throw new ObjectDisposedException(ToString(), "Storage is disposed");
			}
		}

		/// <summary>
		/// Aspect() included
		/// </summary>
		protected void EnsureStorageConnected()
		{
			Aspect();
			if (!Connected)
			{
				throw new InvalidOperationException("Storage is not connected");
			}
		}

		public abstract IEnumerable<Guid> GetObjectIDsCore();
	}
}